package com.base.db.redis

import com.base.config.BaseConfig
import com.base.model.api.APIFrom
import com.google.gson.Gson
import com.yfree.global.YController
import com.yfree.model.YModel
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import java.util.concurrent.TimeUnit

/**
 * database0:无
 * database1:数据库缓存
 * database2:验证码缓存 Token缓存
 * */
@Synchronized
fun RedisService._getRedis(database: Int = 0): RedisTemplate<String, String>? {
    return try {
        val connectionFactory = redisTemplate.connectionFactory as LettuceConnectionFactory
        connectionFactory.database = database
        connectionFactory.afterPropertiesSet()
        connectionFactory.resetConnection()
        redisTemplate
    } catch (e: Exception) {
        null
    }
}

// ==============================================================================================================
// ================================================ 数据库缓存 ===================================================
// ==============================================================================================================
inline fun <reified T : YModel> RedisService.autoCache_model(redisCacheKey: String, key_sub: String? = null, database: Int = 1, timeout: Long? = 5, unit: TimeUnit = TimeUnit.SECONDS, onNewCache: (() -> T?)): T? {
    if (timeout == null || timeout <= 0) return onNewCache.invoke()
    try {
        val key = if (key_sub == null) redisCacheKey else "${redisCacheKey}_${key_sub}"
        //caffeine
        val result_caffeineCache = caffeineCache.getIfPresent(key)
        if (!result_caffeineCache.isNullOrBlank()) return Gson().fromJson(result_caffeineCache, T::class.java)
        //redis
        val redis = _getRedis(database)
        val result_redis = redis?.opsForValue()?.get(key)
        if (!result_redis.isNullOrBlank()) return Gson().fromJson(result_redis, T::class.java)
        //db
        val result_db = onNewCache.invoke()
        if (result_db != null) {
            val result = Gson().toJson(result_db)
            caffeineCache.put(key, result)
            redis?.opsForValue()?.set(key, result, timeout, unit)
        }
        return result_db
    } catch (e: Exception) {
        return onNewCache.invoke()
    }
}

fun <K, V> RedisService.autoCache_map(redisCacheKey: String, key_sub: String? = null, database: Int = 1, timeout: Long? = 5, unit: TimeUnit = TimeUnit.SECONDS, onNoCache: (() -> Map<K, V?>?)): Map<K, V?>? {
    if (timeout == null || timeout <= 0) return onNoCache.invoke()
    try {
        val key = if (key_sub == null) redisCacheKey else "${redisCacheKey}_${key_sub}"
        //caffeine
        val result_caffeineCache = caffeineCache.getIfPresent(key)
        if (!result_caffeineCache.isNullOrBlank()) return Gson().fromJson(result_caffeineCache, Map::class.java) as Map<K, V?>
        //redis
        val redis = _getRedis(database)
        val result_redis = redis?.opsForValue()?.get(key)
        if (!result_redis.isNullOrBlank()) return Gson().fromJson(result_redis, Map::class.java) as Map<K, V?>
        //db
        val result_db = onNoCache.invoke()
        if (result_db != null && !result_db.isNullOrEmpty()) {
            val result = Gson().toJson(result_db)
            caffeineCache.put(key, result)
            redis?.opsForValue()?.set(key, result, timeout, unit)
        }
        return result_db
    } catch (e: Exception) {
        return onNoCache.invoke()
    }
}

fun <T> RedisService.autoCache_list(redisCacheKey: String, key_sub: String? = null, database: Int = 1, timeout: Long? = 5, unit: TimeUnit = TimeUnit.SECONDS, onNoCache: (() -> List<T>?)): List<T>? {
    if (timeout == null || timeout <= 0) return onNoCache.invoke()
    try {
        val key = if (key_sub == null) redisCacheKey else "${redisCacheKey}_${key_sub}"
        //caffeine
        val result_caffeineCache = caffeineCache.getIfPresent(key)
        if (!result_caffeineCache.isNullOrBlank()) return Gson().fromJson(result_caffeineCache, List::class.java) as List<T>
        //redis
        val redis = _getRedis(database)
        val result_redis = redis?.opsForValue()?.get(key)
        if (!result_redis.isNullOrBlank()) return Gson().fromJson(result_redis, List::class.java) as List<T>
        //db
        val result_db = onNoCache.invoke()
        if (result_db != null && !result_db.isNullOrEmpty()) {
            val result = Gson().toJson(result_db)
            caffeineCache.put(key, result)
            redis?.opsForValue()?.set(key, result, timeout, unit)
        }
        return result_db
    } catch (e: Exception) {
        return onNoCache.invoke()
    }
}

fun RedisService.autoCache_string(redisCacheKey: String, key_sub: String? = null, database: Int = 1, timeout: Long? = 5, unit: TimeUnit = TimeUnit.SECONDS, onNoCache: (() -> String?)): String? {
    if (timeout == null || timeout <= 0) return onNoCache.invoke()
    try {
        val key = if (key_sub == null) redisCacheKey else "${redisCacheKey}_${key_sub}"
        //caffeine
        val result_caffeineCache = caffeineCache.getIfPresent(key)
        if (!result_caffeineCache.isNullOrBlank()) return result_caffeineCache
        //redis
        val redis = _getRedis(database)
        val result_redis = redis?.opsForValue()?.get(key)
        if (!result_redis.isNullOrBlank()) return result_redis
        //db
        val result_db = onNoCache.invoke()
        if (!result_db.isNullOrBlank()) {
            caffeineCache.put(key, result_db)
            redis?.opsForValue()?.set(key, result_db, timeout, unit)
        }
        return result_db
    } catch (e: Exception) {
        return onNoCache.invoke()
    }
}

// ==============================================================================================================
// ================================================ 图形验证码缓存 ================================================
// ==============================================================================================================
fun RedisService.cache_addCode_pic(context: YController, loginname: String?, code: String?): Boolean {
    if (loginname.isNullOrBlank() || code.isNullOrBlank()) return false
    _getRedis(2)?.opsForValue()?.set("code_pic_${context::class.java.canonicalName}_${loginname}", code, 300, TimeUnit.SECONDS)
    return true
}

fun RedisService.cache_getCode_pic(context: YController, loginname: String?): String? {
    if (loginname.isNullOrBlank()) return null
    val redis = _getRedis(2)
    val code = redis?.opsForValue()?.get("code_pic_${context::class.java.canonicalName}_${loginname}")
    redis?.delete("code_pic_${context::class.java.canonicalName}_${loginname}")
    return code
}

// ==============================================================================================================
// ================================================ 短信验证码缓存 ================================================
// ==============================================================================================================
fun RedisService.cache_addCode_sms(from: APIFrom, phone: String?, code: String?): Boolean {
    if (phone.isNullOrBlank() || code.isNullOrBlank()) return false
    _getRedis(2)?.opsForValue()?.set("code_sms_${from.name}_${phone}", code, 300, TimeUnit.SECONDS)
    return true
}

fun RedisService.cache_getCode_sms(from: APIFrom, phone: String?): String? {
    if (phone.isNullOrBlank()) return null
    val redis = _getRedis(2)
    val code = redis?.opsForValue()?.get("code_sms_${from.name}_${phone}")
    redis?.delete("code_sms_${from.name}_${phone}")
    return code
}

// ==============================================================================================================
// ============================================= RefreshToken缓存 ================================================
// ==============================================================================================================
fun RedisService.cache_addToken_refresh(from: APIFrom, refreshToken: String?, uid: String?): Boolean {
    if (refreshToken.isNullOrBlank() || uid.isNullOrBlank()) return false
    _getRedis(2)?.opsForValue()?.set("token_refresh_${from.name}_${refreshToken}", uid, BaseConfig.refreshTokenExpire, TimeUnit.SECONDS)
    return true
}

fun RedisService.cache_getToken_refresh(from: APIFrom, refreshToken: String?): String? {
    if (refreshToken.isNullOrBlank()) return null
    val redis = _getRedis(2)
    val code = redis?.opsForValue()?.get("token_refresh_${from.name}_${refreshToken}")
    redis?.delete("token_refresh_${from.name}_${refreshToken}")
    return code
}
